package com.task.manager.config.quartz;

import java.net.InetAddress;
import java.time.Instant;
import java.util.List;

import org.apache.http.client.utils.DateUtils;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import com.task.manager.domain.Lock;
import com.task.manager.domain.Task;
import com.task.manager.domain.TaskResult;
import com.task.manager.enums.TaskType;
import com.task.manager.mapper.LockMapper;
import com.task.manager.mapper.TaskMapper;
import com.task.manager.mapper.TaskResultMapper;
import com.task.manager.util.TaskContants;

public abstract class AbstractJob implements Job {
	private static final Logger logger = LoggerFactory.getLogger(AbstractJob.class);
	@Autowired
	private LockMapper lockMapper;
	@Autowired
	private TaskMapper taskMapper;
	@Autowired
	private TaskResultMapper taskResultMapper;
	@Override
	public void execute(JobExecutionContext context) throws JobExecutionException {
		String taskCode = context.getJobDetail().getKey().getName();
		try {
			if (StringUtils.isEmpty(taskCode)) {
				logger.error("AbstractJob_获取taskCode为空");
				return;
			}
			if (!isLock(taskCode)) {
				try {
					Task task = taskMapper.selectByTaskCode(taskCode);
					Integer count = task.getRetryCount();
					if(count == null || count == 0) {
						count = 1;
					}
					TaskType taskType = null;
					if(!StringUtils.isEmpty(task.getTaskType())) {
						taskType = TaskType.valueOf(task.getTaskType());
					}
					for (int i = 1; i <= count ; i++) {
						String batchNo = taskCode+"_"+DateUtils.formatDate(context.getFireTime(), "yyyyMMddHHmmss")+"_"+i;
						List<TaskResult> results = taskResultMapper.queryByBatchNo(batchNo);
						if(!CollectionUtils.isEmpty(results)) {
							logger.info("任务"+taskCode+"已经执行过,批次号:"+batchNo);
							break;
						}
						TaskResult taskResult = new TaskResult();
						taskResult.setRetryNum(i);
						taskResult.setTaskCode(task.getTaskCode());
						taskResult.setTaskHeader(task.getTaskHeader());
						taskResult.setTaskName(task.getTaskName());
						taskResult.setTaskParam(task.getTaskParam());
						taskResult.setTaskUrl(task.getTaskUrl());
						taskResult.setActuatorCode(task.getActuatorCode());
						InetAddress address = InetAddress.getLocalHost();
						taskResult.setIpAddress(address.getHostAddress());
						taskResult.setBatchNo(batchNo);
						taskResult.setExecuteTime(context.getFireTime());
						taskResult.setNextTime(context.getNextFireTime());
						taskResultMapper.insert(taskResult);
						executeWithLock(task,taskResult,taskType);
						taskResultMapper.updateByPrimaryKey(taskResult);
						if(taskResult.getResult()) {
							break;
						}
					}
				} catch (Exception e) {
					logger.error("AbstractJob_执行任务失败：" + e.getMessage(), e);
				}
				releaseLock(taskCode);
			} else {
				logger.info("当前任务" + taskCode + "已有其他执行器执行");
			}
		} catch (Exception e) {
			logger.error("AbstractJob_execute_error,错误任务代码：" + taskCode + ",错误信息：" + e.getMessage(), e);
		}
	}

	/**
	 * 判断数据库是否有锁
	 * 
	 * @param taskCode
	 * @return
	 */
	private boolean isLock(String taskCode) {
		try {
			Lock lock = lockMapper.selectWithExpTime(taskCode, Instant.now().toEpochMilli());
			if (lock == null) {
				lock = new Lock();
				lock.setLockTime(Instant.now().toEpochMilli());
				lock.setExpTime(TaskContants.LOCK_EXPEIRE_TIME);
				lock.setLockCode(taskCode);
				int i = lockMapper.insertIngnore(lock);
				if (i == 0) {
					Lock dbLock = lockMapper.selectByPrimaryKey(taskCode);
					if (dbLock != null && Instant.now().toEpochMilli() - dbLock.getLockTime() > dbLock.getExpTime()) {
						logger.info("当前任务" + taskCode + "的锁已过期，即将释放锁，锁时间：" + dbLock.getLockTime() + ",过期时间：" + dbLock.getExpTime());
						releaseLock(taskCode);
						i = lockMapper.insertIngnore(lock);
					}
				}
				return i != 1;
			}
			return true;
		} catch (Exception e) {
			logger.error("AbstractJob_判定是否有锁：" + e.getMessage(), e);
			return true;
		}
	}

	private void releaseLock(String lockCode) {
		try {
			lockMapper.deleteByPrimaryKey(lockCode);
		} catch (Exception e) {
			logger.error("AbstractJob_释放锁失败：" + e.getMessage(), e);
		}
	}

	public abstract void executeWithLock(Task task,TaskResult taskResult,TaskType taskType);
}
